import 'dart:io';
import 'package:flutter_client_sse/constants/sse_request_type_enum.dart';
import 'package:flutter_client_sse/flutter_client_sse.dart';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:get/get_utils/get_utils.dart';

import '../models/index.dart';
import '../widgets/index.dart';
import 'loading.dart';
import 'log_util.dart';
import 'o2_api_manager.dart';
import 'o2_utils.dart';

class O2Interceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    // print("\n================================= 请求数据 =================================");
    // print("method = ${options.method.toString()}");
    // print("url = ${options.uri.toString()}");
    // print("headers = ${options.headers}");
    // print("params = ${options.queryParameters}");
    super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    //  print(
    //       "\n================================= 响应数据开始 =================================");
    //   print("code = ${response.statusCode}");
    //   print("data = ${response.data}");
    //   print(
    //       "================================= 响应数据结束 =================================\n");
    super.onResponse(response, handler);
  }

  @override
  onError(DioError err, ErrorInterceptorHandler handler) {
    try {
      var code = err.response?.statusCode;
      if (code == 500) {
        List<String>? type = err.response?.headers['Content-Type'];
        if (type != null &&
            (type.contains('application/json') ||
                type.contains('application/json;charset=utf-8'))) {
          ApiResponse a = ApiResponse.fromJson(err.response?.data);
          if (a.message != null && a.message!.isNotEmpty) {
            Loading.toast(a.message!);
          }
          return super.onError(
              DioError(
                  requestOptions: err.requestOptions,
                  response: err.response,
                  error: a.message),
              handler);
        }
      } else if (err.response?.statusCode == 401) {
        // token过期 需要重新登录
        OLogger.e('401 异常 ${err.response.toString()}');
        O2OverlayEntryDialog.instance.openAlertDialog(
            'error'.tr, 'common_session_timeout'.tr,
            callback: () => O2Utils.logout(hasToken: false));
      } else {
        OLogger.e(err);
      }
    } catch (e) {
      OLogger.e(e);
    }
    // Loading.toast('request_error'.tr);
    return super.onError(err, handler);
  }
}

typedef O2HttpSSECallback = void Function(SSEModel model);

class O2HttpClient {
  static final O2HttpClient instance = O2HttpClient._internal();

  factory O2HttpClient() {
    return instance;
  }

  O2HttpClient._internal() {
    //初始化
    _dio = Dio();
    _dio.interceptors.add(O2Interceptor());
    // 忽略 https 证书验证
    // (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
    //   client.badCertificateCallback = (X509Certificate cert, String host, int port) => true;
    //   return client;
    // };
  }

  late Dio _dio;
  get dio {
    return _dio;
  }

  void endSSE() async {
    SSEClient.unsubscribeFromSSE();
  }

  Future<void> postSSE(
      String url, Map<String, dynamic> data, O2HttpSSECallback callback) async {
    Map<String, String> headers = <String, String>{};
    headers['Accept'] = 'text/event-stream';
    headers['Cache-Control'] = 'no-cache';
    headers['content-type'] = 'application/json; charset=utf-8';
    headers['x-client'] = _xClient();
    if (O2ApiManager.instance.o2User != null) {
      headers[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    OLogger.d('开始 SSE url：$url');
    SSEClient.subscribeToSSE(
            method: SSERequestType.POST, url: url, header: headers, body: data)
        .listen((event) {
      OLogger.d('======>postSSE 接收到 ${event.id} ${event.event} ${event.data}');
      callback(event);
    }, onDone: () {
      OLogger.i('======>postSSE OnDone ');
      //callback(O2.SSE_DONE);
    }, onError:(err, stackTrace) {
      OLogger.e('', err, stackTrace);
      //callback(O2.SSE_DONE);
    });
  }
  // Future<void> postSSE(
  //     String url, Map<String, dynamic> data, O2HttpSSECallback callback) async {
  //   final client = http.Client();
  //   final request = http.Request('POST', Uri.parse(url));
  //   request.headers['Accept'] = 'text/event-stream';
  //   request.headers['Cache-Control'] = 'no-cache';
  //   request.headers['content-type'] = 'application/json; charset=utf-8';
  //   request.headers['x-client'] = _xClient();
  //   if (O2ApiManager.instance.o2User != null) {
  //     request.headers[O2ApiManager.instance.tokenName] =
  //         O2ApiManager.instance.o2User?.token ?? '';
  //   }
  //   request.body = jsonEncode(data);
  //   OLogger.d('开始 SSE url：$url ${request.headers}');
  //   final response = await client.send(request);
  //   if (response.statusCode == 200) {
  //     response.stream
  //         .transform(utf8.decoder)
  //         .transform(const LineSplitter())
  //         .listen((line) {
  //       OLogger.d('======>postSSE 接收到 $line');
  //       callback(line);
  //     }, onDone: () {
  //       OLogger.i('======>postSSE OnDone ');
  //       //callback(O2.SSE_DONE);
  //     }, onError: (err, stackTrace) {
  //       OLogger.e('', err, stackTrace);
  //       //callback(O2.SSE_DONE);
  //     }, cancelOnError: true);
  //   } else {
  //     OLogger.e('SSE 错误 ${response.statusCode}');
  //   }
  // }

  ///
  /// post方法 json
  ///
  Future<ApiResponse> post(String url, Map<String, dynamic> data,
      {bool needToken = true}) async {
    Response<dynamic> response =
        await _dio.post(url, data: data, options: _postJsonOptions(needToken));
    return ApiResponse.fromJson(response.data);
  }

  ///
  /// get方法 json
  ///
  Future<ApiResponse> get(String url,
      {bool needToken = true, CancelToken? cancelToken}) async {
    Response<dynamic> response = await _dio.get(url,
        options: _getJsonOptions(needToken), cancelToken: cancelToken);
    return ApiResponse.fromJson(response.data);
  }

  /// 不转 ApiResponse  对象 外部使用的
  Future<Response<dynamic>> getOuter(String url) async {
    return await _dio.get(url, options: _getJsonOptions(false));
  }

  getWithCallback(String url, Function(ApiResponse?, dynamic) callback,
      {bool needToken = true}) async {
    _dio.get(url, options: _getJsonOptions(needToken)).then((response) {
      OLogger.d('执行了then');
      callback(ApiResponse.fromJson(response.data), null);
    }).catchError((e) {
      OLogger.d('执行了异常');
      callback(null, e);
    });
  }

  ///
  /// put 方法 json
  ///
  Future<ApiResponse> put(String url, Map<String, dynamic> data,
      {bool needToken = true, CancelToken? cancelToken}) async {
    var mock = _needChange2Mock(url, "put");
    if (mock != null) {
      var newUrl = "$url/${mock.append}";
      var to = mock.to;
      if (to != null && to.isNotEmpty && to.toLowerCase() == 'post') {
        // put 方法转 post
        OLogger.d('==========> put 方法转 post, 转化地址为 $newUrl');
        Response<dynamic> response = await _dio.post(newUrl,
            data: data, options: _putJsonOptions(needToken, newMethod: to));
        return ApiResponse.fromJson(response.data);
      }
    }
    Response<dynamic> response = await _dio.put(url,
        data: data,
        options: _putJsonOptions(needToken),
        cancelToken: cancelToken);
    return ApiResponse.fromJson(response.data);
  }

  ///
  /// delete方法 json
  ///
  Future<ApiResponse> delete(String url, {bool needToken = true}) async {
    var mock = _needChange2Mock(url, "delete");
    if (mock != null) {
      var newUrl = "$url/${mock.append}";
      var to = mock.to;
      if (to != null && to.isNotEmpty) {
        if (to.toLowerCase() == 'get') {
          // delete 方法转 get
          OLogger.d('==========> delete 方法转 get, 转化地址为 $newUrl');
          Response<dynamic> response = await _dio.get(newUrl,
              options: _deleteJsonOptions(needToken, newMethod: to));
          return ApiResponse.fromJson(response.data);
        } else if (to.toLowerCase() == 'post') {
          // delete 方法转 post
          OLogger.d('==========> delete 方法转 post, 转化地址为 $newUrl');
          Response<dynamic> response = await _dio.post(newUrl,
              data: null,
              options: _deleteJsonOptions(needToken, newMethod: to));
          return ApiResponse.fromJson(response.data);
        }
      }
    }
    Response<dynamic> response =
        await _dio.delete(url, options: _deleteJsonOptions(needToken));
    return ApiResponse.fromJson(response.data);
  }

  ///
  /// PUT方法上传文件
  ///
  Future<ApiResponse> putUploadFile(String url, File file,
      {Map<String, dynamic>? body, ProgressCallback? onSendProgress}) async {
    var fileName = _filename(file);
    Map<String, dynamic> form = {};
    if (body != null) {
      form = body;
    }
    form["file"] = await MultipartFile.fromFile(file.path, filename: fileName);
    FormData formData = FormData.fromMap(form);
    // FormData formData = FormData.fromMap({
    //   "file": await MultipartFile.fromFile(file.path, filename: fileName),
    // });
    Response<dynamic> response = await _dio.put(url,
        data: formData,
        options: _putJsonOptions(true),
        onSendProgress: onSendProgress);
    return ApiResponse.fromJson(response.data);
  }

  ///
  /// POST方法上传文件
  ///
  Future<ApiResponse> postUploadFile(String url, File file,
      {Map<String, dynamic>? body, ProgressCallback? onSendProgress}) async {
    var fileName = _filename(file);
    Map<String, dynamic> form = {};
    if (body != null) {
      form = body;
    }
    form["file"] = await MultipartFile.fromFile(file.path, filename: fileName);
    FormData formData = FormData.fromMap(form);
    Response<dynamic> response = await _dio.post(url,
        data: formData,
        options: _postJsonOptions(true),
        onSendProgress: onSendProgress);
    return ApiResponse.fromJson(response.data);
  }

  ///
  /// 下载文件
  ///
  Future<Response> downloadFile(String url, String savePath,
      {ProgressCallback? onReceiveProgress}) async {
    Map<String, String> header = {};
    header['x-client'] = _xClient();
    if (O2ApiManager.instance.o2User != null) {
      header[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    header[HttpHeaders.acceptEncodingHeader] = "*";
    Options options = Options(headers: header);
    Response response = await _dio.download(url, savePath,
        onReceiveProgress: onReceiveProgress, options: options);
    return response;
  }

  ///
  /// 下载文件
  /// 不知道文件名称的情况
  ///
  Future<String> downloadFileNoFileName(String url, String saveFolderPath,
      {ProgressCallback? onReceiveProgress}) async {
    Map<String, String> header = {};
    header['x-client'] = _xClient();
    if (O2ApiManager.instance.o2User != null) {
      header[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    header[HttpHeaders.acceptEncodingHeader] = "*";
    Options options = Options(headers: header);
    String fileLocalPath = '';
    await _dio.download(url, (Headers headers) {
      var fileName = headers.value("Content-Disposition");
      if (fileName != null && fileName.isNotEmpty) {
        // RegExp filenameRegExp = RegExp(r'filename="([^"]+)"');
        RegExp filenameRegExp = RegExp(r"filename\*=UTF-8''([^;]+)");
        Match? filenameMatch = filenameRegExp.firstMatch(fileName);
        if (filenameMatch != null) {
          fileName = filenameMatch.group(1) ?? '';
          fileName = Uri.decodeComponent(fileName);
        }
      }
      if (fileName == null || fileName.isEmpty == true) {
        fileName = 'unknow';
      }
      fileLocalPath = '$saveFolderPath/$fileName';
      return fileLocalPath;
    }, onReceiveProgress: onReceiveProgress, options: options);
    return fileLocalPath;
  }

  String _filename(File file) {
    if (file.existsSync()) {
      var index = file.path.lastIndexOf(Platform.pathSeparator);
      return file.path.substring(index + 1, file.path.length);
    }
    return '';
  }

  ///
  ///是否需要替换方法类型
  ///比如 put转post
  ///
  MockMode? _needChange2Mock(String url, String currentMethod) {
    var mock = O2ApiManager.instance.centerServerInfo?.mockConfig?.mock;
    MockMode? mm;
    if (mock != null) {
      for (var element in mock.keys) {
        if (url.contains(element)) {
          // 判断是需要转化的模块
          var mode = mock[element];
          for (var method in mode.keys) {
            if (currentMethod.toLowerCase() == method.toLowerCase()) {
              // 请求方法相同
              mm = MockMode.fromJson(mode[method]);
              continue;
            }
          }
          continue;
        }
      }
    }
    return mm;
  }

  Options _deleteJsonOptions(bool needToken, {String? newMethod}) {
    Map<String, String> header = <String, String>{};
    header['x-client'] = _xClient();
    if (needToken && O2ApiManager.instance.o2User != null) {
      header[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    return Options(
        method: newMethod == null || newMethod.isEmpty ? 'DELETE' : newMethod,
        headers: header,
        contentType: ContentType.json.mimeType);
  }

  Options _getJsonOptions(bool needToken) {
    Map<String, String> header = <String, String>{};
    header['x-client'] = _xClient();
    if (needToken && O2ApiManager.instance.o2User != null) {
      header[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    return Options(
        method: 'GET', headers: header, contentType: ContentType.json.mimeType);
  }

  Options _postJsonOptions(bool needToken) {
    Map<String, String> header = <String, String>{};
    header['x-client'] = _xClient();
    if (needToken && O2ApiManager.instance.o2User != null) {
      header[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    return Options(
        method: 'POST',
        headers: header,
        contentType: ContentType.json.mimeType);
  }

  Options _putJsonOptions(bool needToken, {String? newMethod}) {
    Map<String, String> header = <String, String>{};
    header['x-client'] = _xClient();
    if (needToken && O2ApiManager.instance.o2User != null) {
      header[O2ApiManager.instance.tokenName] =
          O2ApiManager.instance.o2User?.token ?? '';
    }
    return Options(
        method: newMethod == null || newMethod.isEmpty ? 'PUT' : newMethod,
        headers: header,
        contentType: ContentType.json.mimeType);
  }

  String _xClient() {
    String xClient = "flutter";
    if (Platform.isAndroid) {
      xClient = 'android';
    } else if (Platform.isIOS) {
      xClient = 'ios';
    }
    return xClient;
  }
}
